package com.apache.ius.common.annotion;


import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * description:
 *
 * @author dyh 创建时间：2018年07月02日
 */
public class PkgScanner {
    /**
     * 包名
     */
    private String pkgName;

    /**
     * 包对应的路径名
     */
    private String pkgPath;

    /**
     * 注解的class对象
     */
    private Class anClazz;

    private ClassLoader cl;


    public PkgScanner(String pkgName) {
        this.pkgName = pkgName;
        this.pkgPath = AnnotationUtil.packageToPath(pkgName);

        cl = Thread.currentThread().getContextClassLoader();
    }

    public PkgScanner(String pkgName, Class anClazz) {
        this(pkgName);
        this.anClazz = anClazz;
    }

    /**
     * 执行扫描操作.
     *
     * @return
     * @throws IOException
     */
    public List<String> scan() throws IOException {
        List<String> list = loadResource();
        if (null != this.anClazz) {
            list = filterComponents(list);
        }

        return list;
    }

    public void setPkgName(String pkgName) {
        this.pkgName = pkgName;
        this.pkgPath = AnnotationUtil.packageToPath(pkgName);
    }

    public void setAnnocation(Class an) {
        this.anClazz = an;
    }

    private List<String> loadResource() throws IOException {
        List<String> list = null;

        Enumeration<URL> urls = cl.getResources(pkgPath);
        while (urls.hasMoreElements()) {
            URL u = urls.nextElement();

            String type =u.getProtocol();
            String filePath = AnnotationUtil.getURLEncoderString(u.getPath().replace("%5c","/"));
            switch (type) {
            case "jar":
                String path = AnnotationUtil.distillPathFromJarURL(filePath);
                list = scanJar(path);
                break;

            case "file":
                list = scanFile(filePath, pkgName);
                break;
            }
        }
        return list;
    }

    /**
     * 扫描JAR文件
     * @param path
     */
    private List<String> scanJar(String path) throws IOException {
        JarFile jar =null;
        try {
            jar = new JarFile(path);
            List<String> classNameList = new ArrayList<>();
            Enumeration<JarEntry> entries = jar.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String name = entry.getName();
                if ((name.startsWith(pkgPath)) && (name.endsWith(".class"))) {
                    name = AnnotationUtil.trimSuffix(name);
                    name = AnnotationUtil.pathToPackage(name);
                    classNameList.add(name);
                }
            }
            return classNameList;
        }finally {
            if(null != jar){
                jar.close();
            }
        }
    }

    /**
     * 扫描文件目录下的类
     * @param path
     * @return
     */
    private List<String> scanFile(String path, String basePkg) {
        File f = new File(path);

        List<String> classNameList = new ArrayList<>();

        // 得到目录下所有文件(目录)
        File[] files = f.listFiles();
        if (null != files) {
            int LEN = files.length;

            for (int ix = 0 ; ix < LEN ; ++ix) {
                File file = files[ix];

                // 判断是否还是一个目录
                if (file.isDirectory()) {
                    // 递归遍历目录
                    List<String> list = scanFile(file.getAbsolutePath(), AnnotationUtil.concat(basePkg, ".", file.getName()));
                    classNameList.addAll(list);

                } else if (file.getName().endsWith(".class")) {
                    // 如果是以.class结尾
                    String className = AnnotationUtil.trimSuffix(file.getName());
                    // 如果类名中有"$"不计算在内
                    if (-1 != className.lastIndexOf("$")) {
                        continue;
                    }

                    // 命中
                    String result = AnnotationUtil.concat(basePkg, ".", className);
                    classNameList.add(result);
                }
            }
        }

        return classNameList;
    }

    /**
     * 过虑掉没有指定注解的类
     * @param classList
     * @return
     */
    private List<String> filterComponents(List<String> classList) {
        List<String> newList = new ArrayList<>();
        if(null==classList){
            return newList;
        }
        for(String name : classList){
            try {
                Class clazz = Class.forName(name);
                Annotation an = clazz.getAnnotation(this.anClazz);
                if (null != an) {
                    newList.add(name);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return newList;
    }
}
